﻿using Red.CommUtils;
using Red.EngineData.Protocol;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BeefSandbox
{
    internal class FakeViewAccessService
    {
        private bool _areMessagesSuppressed = false;
        private List<Tuple<comm.Message, IResponse>> _suppressedMessages = new List<Tuple<comm.Message, IResponse>>();
        private bool _serviceClosed = false;
        private IChannel _channel;
        private FakeDataStructure _rootData;
        private Random _random = new Random();

        internal class FakeDataStructure
        {
            public string Name;
            public List<FakeDataStructure> Children;

            public string __type = "";
            public string __class = "";
            public string __value = "";

            public FakeDataStructure(string name)
            {
                Name = name;
                Children = new List<FakeDataStructure>();
            }

        }

        public FakeViewAccessService(IChannel channel)
        {
            _channel = channel;
            _channel.RegisterListener(OnMessage);
            BuildFakeEngineData();
        }

        private void BuildFakeEngineData()
        {
            _rootData = new FakeDataStructure( "console" )
            {
                __type = "compound",
                __class = "EngineView",
                Children = {
                    new FakeDataStructure("Viewports")
                    {
                        __type = "array",
                        __class = "List<Viewport>",
                        Children = {
                            new FakeDataStructure("Viewport_Mesh1234")
                            {
                                __type = "compound",
                                __class = "Viewport",
                                Children = {
                                    new FakeDataStructure("SelectedObject")
                                    {
                                        __type = "compound",
                                        __class = "ObjectView",
                                        Children = {
                                            new FakeDataStructure("Entity_1234")
                                            {
                                                __type = "compound",
                                                __class = "EntityView",
                                                Children = {
                                                    new FakeDataStructure("Name")
                                                    {
                                                        __type = "simple",
                                                        __class = "string",
                                                        __value = "R2D2"
                                                    }
                                                }
                                            }
                                        }
                                    },
                                    new FakeDataStructure("Settings")
                                    {
                                        __type = "compound",
                                        __class = "SettingsView",
                                        Children = {
                                            new FakeDataStructure("DisplayMode")
                                            {
                                                __type = "simple",
                                                __class = "EDisplayMode",
                                                __value = "Solid"
                                            },
                                            new FakeDataStructure("ShowGrid")
                                            {
                                                __type = "simple",
                                                __class = "Bool",
                                                __value = "true"
                                            },
                                            new FakeDataStructure("ShowFPS")
                                            {
                                                __type = "simple",
                                                __class = "Bool",
                                                __value = "false"
                                            },
                                            new FakeDataStructure("RandomizeWorld")
                                            {
                                                __type = "simple",
                                                __class = "Bool",
                                                __value = "false"
                                            },
                                            new FakeDataStructure("Awesome")
                                            {
                                                __type = "simple",
                                                __class = "Bool",
                                                __value = "true"
                                            },
                                            new FakeDataStructure("SpecialProperty")
                                            {
                                                __type = "simple",
                                                __class = "Bool",
                                                __value = "false"
                                            },
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            };

            FakeDataStructure bigContainer = new FakeDataStructure("BigContainer") { __type = "compound", __class = "List" };
            const int elementCount = 1000;
            for (int i = 0; i < elementCount; ++i)
            {
                bigContainer.Children.Add(new FakeDataStructure(i.ToString()) { __type = "simple", __class = "string", __value = "test value" });
            }

            _rootData.Children.Add(bigContainer);
        }

        private void WaitRandomAmountOfTime()
        {
            int randomTime = _random.Next(0, 1000);
            System.Threading.Thread.Sleep(randomTime);
        }

        private void OnMessage(comm.Message message, IResponse response)
        {
            WaitRandomAmountOfTime();

            if (_serviceClosed == false)
            {
                if (_areMessagesSuppressed)
                {
                    _suppressedMessages.Add(new Tuple<comm.Message, IResponse>(message, response));
                }
                else
                {
                    if (message.IsA(comm.EMessageID.InteropCall))
                        OnInteropCallMethod( (comm.InteropCall)message, response );
                    else if (message.IsA(comm.EMessageID.CloseService))
                        OnCloseService((comm.CloseService)message, response);
                }
            }
        }

        private bool FindDataStructure(FakeDataStructure parent, string name, out FakeDataStructure result)
        {
            foreach (FakeDataStructure child in parent.Children)
            {
                if (child.Name == name)
                {
                    result = child;
                    return true;
                }
            }

            result = null;
            return false;
        }

        private bool FindDataStructureFromPath(string path, out FakeDataStructure result)
        {
            string[] tokens = path.Split('.');     // TODO: add array support

            FakeDataStructure current = _rootData;
            foreach (string token in tokens)
            {
                if ( token == "console" )
                {
                    current = _rootData;
                }
                else if (FindDataStructure(current, token, out current) == false)
                {
                    result = null;
                    return false;
                }
            }

            result = current;
            return true;
        }

        private void OnInteropCallMethod(comm.InteropCall message, IResponse response)
        {
            string expr = message.expr;

            if ( expr.EndsWith( "= {0}" ) )
            {
                if (message.args.Count == 0)
                    SendInvalidInteropException(response);
                else
                    SendSetValueResponse( expr, message.args[0], response );
            }
            else
            {
                SendGetResponse( expr, response );
            }
        }

        public void SendGetResponse(string path, IResponse response)
        {
            FakeDataStructure structure;
            if ( FindDataStructureFromPath( path, out structure ) == false )
            {
                SendInvalidInteropException(response);
                return;
            }

            comm.InteropBlob result = new comm.InteropBlob();
            result.scalarValue = structure.__value;
            result.metadata = "type=" + structure.__class;
            foreach (FakeDataStructure child in structure.Children)
            {
                comm.InteropPropertyInfo childResult = new comm.InteropPropertyInfo();
                childResult.name = child.Name;
                childResult.metadata = "type=" + child.__class;
                if ( child.Children.Count > 0 )
                    childResult.metadata += ";expandable=true";

                result.propertyInfos.Add( childResult );
            }

            response.Send(result);
        }

        public void SendSetValueResponse(string path, string value, IResponse response)
        {
            FakeDataStructure structure;
            if (FindDataStructureFromPath(path, out structure) == false)
            {
                SendInvalidInteropException(response);
                return;
            }

            structure.__value = value;

            comm.InteropBlob result = new comm.InteropBlob();
            result.scalarValue = structure.__value;
            result.metadata = "type=" + structure.__class;
            response.Send(result);
        }

        public void SendInvalidInteropException(IResponse response)
        {
            comm.InteropException exception = new comm.InteropException();
            exception.text = "Invalid interop";
            response.Send(exception);
        }

        private void OnCloseService(comm.CloseService message, IResponse response)
        {
            _serviceClosed = true;
        }
    }
}
